﻿#include <iostream>
#include <assert.h>
//6. 引用

//引用不是新定义一个变量，而是给已存在变量取了一个别名，
//编译器不会为引用变量开辟内存空间，它和它引用的变量共用同一块内存空间。

using namespace std;
//int main() {
//	//下方这里是引用。本质就是同一个空间有两个名字
//	int a = 0;
//	int& b = a;//<====定义引用类型
//
//	cout << &a << endl;//这里是取地址
//	cout << &b << endl;
//
//	b++;//b++可以让a也++
//	a++;//a++可以让b也++
//}



////引用的示例1
//struct TreeNode {
//	int val;
//	struct TreeNode* left;
//	struct TreeNode* right;
//};
//
//int TreeSize(struct TreeNode* root) {
//	return !root ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
//}

////C语言版本
//void _preorder(struct TreeNode* root, int* a, int* pi) { //用指针接收
//	if (!root)
//		return;
//
//	a[(*pi)++] = root->val;//对指针解引用
//	_preorder(root->left, a, pi);
//	_preorder(root->right, a, pi);
//}
//int* preorderTraversal(struct TreeNode* root, int* returnSize) {
//	*returnSize = TreeSize(root);
//	int* a = (int*)malloc(*returnSize * sizeof(int));
//
//	int i = 0;
//	_preorder(root, a, &i);//传地址
//
//	return a;
//}

////C++引用版本
//void _preorder(struct TreeNode* root, int* a, int& ri) { //ri是i的别名，修改ri就可以修改i
//	if (!root)
//		return;
//
//	a[ri++] = root->val;
//	_preorder(root->left, a, ri);
//	_preorder(root->right, a, ri);
//}
//
//int* preorderTraversal(struct TreeNode* root, int& returnSize) { //输出型参数
//	returnSize = TreeSize(root);
//	int* a = (int*)malloc(returnSize * sizeof(int));
//
//	int i = 0;
//	_preorder(root, a, i);//C++引用版本
//	return a;
//}
//
//int main() {
//	int size;
//	preorderTraversal(nullptr, size);
//}


////引用的示例2
//void Swap(int& x1, int& x2) { //指针修改
//	int tmp = x1;
//	x1 = x2;
//	x2 = tmp;
//}
//int main() {
//	int x = 0, y = 1;
//	Swap(x, y);
//}


//引用的示例3
//typedef struct ListNode {
//	int val;
//	struct ListNode* next;
//}ListNode;
////错误版本
////这里是传值调用，phead是plist的临时拷贝。修改phead不会影响plist
//void PushBack(ListNode* phead, int x) {}

////C语言版本
//void PushBack(ListNode** pphead, int x) {} //修改plist需要plist的地址。plist是一级指针，所以需要传二级指针

////C++引用版本
//typedef struct ListNode {
//	int val;
//	struct ListNode* next;
//}ListNode, *PListNode;
//void PushBack(ListNode*& pphead, int x) {} //写法1 建议这种写法，能够清楚是一级指针的引用
//void PushBack(PListNode& pphead, int x) {} //写法2
//int main() {
//	ListNode* plist = NULL;
//	//PushBack(plist, 1);//错误版本
//	//PushBack(&plist, 1);//C语言版本
//	PushBack(plist, 1);
//	return 0;
//}



////6.2 引用特性
////1. 引用在定义时必须初始化
////2. 一个变量可以有多个引用
////3. 引用一旦引用一个实体，再不能引用其他实体
//int main() {
//	//int& b;//错误。引用在定义时必须初始化
//
//	int a = 0;//一个变量可以有多个引用
//	int& b = a;
//	int& c = a;
//	int& d = b;//可以对引用再引用
//
//	int x = 1;
//	b = x;//这里不是引用，是x的值赋值给b（因为一旦引用一个实体，再不能引用其他实体）。abcd的值都被修改了
//	//int& b = x;//重定义，错误。b引用了a，不能再引用其他实体
//}



//6.3 常引用
 //在引用的过程中
 //权限可以平移、缩小，但不能放大
int func() {
	int a = 0;
	return a;
}
void TestConstRef() {
	int ret = func();//并不是直接返回a，而是有个临时变量（具有常性）。ret的改变不影响该临时变量
	//int& ret2 = func();//错误版本。根据上述原因，不能引用常量。
	const int& ret2 = func();

	const int a = 10;
	//int& ra = a;   // 权限的放大。该语句编译时会出错，a为常量
	const int& ra = a;//权限的平移
	int b = a;//这里是赋值拷贝，b修改不影响a

	int x = 0;
	const int& y = x; //权限的缩小。x可以修改，修改后会影响y，但y不能修改。

	//int& b = 10; // 该语句编译时会出错，b为常量。只能对变量引用
	const int& b = 10;
	
	int i = 0;
	double j = i;//可以赋值，

	double d = 12.34;
	//int& rd = d; //发生类型转换时，有个临时变量，临时变量具有常性，所以不能引用常量。该语句编译时会出错，类型不同
	const int& rd = d;//加const修饰就变为常量，可以引用
}



////6.4 使用场景
////1. 做参数
//void Swap(int& left, int& right) {
//	int temp = left;
//	left = right;
//	right = temp;
//}

//2. 做返回值

////传值返回
//int Count() {
//	static int n = 0;
//	n++;
//	// ...
//	return n;
//}
//int main() {
//	int ret = Count();
//	//这里其实不是返回n，因为n已经虽然Count函数栈帧销毁而销毁了。
//	//中间有个临时变量储存返回值
//}

////传引用返回
//int& Count() { //错误示范
//	int n = 0;
//	n++;
//	// ...
//	return n;//返回n的别名。有点类似野指针
//}
//int main() {
//	//int ret = Count();//这种是n的引用拷贝给ret
//	//cout << ret << endl;//这里打印的结果可能是1，也可能是随机值
//	//cout << ret << endl;//同上方
//
//	//下方两次打印结果可能是1，也可能是随机值
//	//因为ret是n指向空间的引用。但n和Count函数已经被销毁了
//	//cout是函数调用，所以n指向那块空间可能就被覆盖了
//	int& ret = Count();
//	cout << ret << endl;
//	cout << ret << endl;
//}

////下面代码输出什么结果？为什么？
//int& Add(int a, int b) { //错误示范
//	int c = a + b;
//	return c;
//}
//int main() {
//	int& ret = Add(1, 2);
//	Add(3, 4);
//	cout << "Add(1, 2) is :" << ret << endl;//结果可能是7
//	//因为ret是c空间的引用。第二次调用Add函数，c的值被修改为7
//	return 0;
//}
////所以上述这些函数不能使用引用返回，可能造成随机值
//
////注意：如果函数返回时，出了函数作用域，如果返回对象还在(还没还给系统)，则可以使用引用返回，
////如果已经还给系统了，则必须使用传值返回。



////6.5 传值、传引用效率比较
////以值作为参数或者返回值类型，在传参和返回期间，函数不会直接传递实参或者将变量本身直接返回，
////而是传递实参或者返回变量的一份临时的拷贝，因此用值作为参数或者返回值类型，
////效率是非常低下的，尤其是当参数或者返回值类型非常大时，效率就更低。
//#include <time.h>
//struct A { int a[10000]; };
//void TestFunc1(A a) {}
//void TestFunc2(A& a) {}
//void TestRefAndValue() {
//	A a;
//	// 以值作为函数参数
//	size_t begin1 = clock();
//	for (size_t i = 0; i < 10000; ++i)
//		TestFunc1(a);
//	size_t end1 = clock();
//	// 以引用作为函数参数
//	size_t begin2 = clock();
//	for (size_t i = 0; i < 10000; ++i)
//		TestFunc2(a);
//	size_t end2 = clock();
//	// 分别计算两个函数运行结束后的时间
//	cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;
//	cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
//}
//int main() {
//	TestRefAndValue();
//}

////6.5.2 值和引用的作为返回值类型的性能比较
//#include <time.h>
//struct A { int a[10000]; };
//A a;
//// 值返回
//A TestFunc1() { return a; }
//// 引用返回
//A& TestFunc2() { return a; }
//void TestReturnByRefOrValue() {
//	// 以值作为函数的返回值类型
//	size_t begin1 = clock();
//	for (size_t i = 0; i < 100000; ++i)
//		TestFunc1();
//	size_t end1 = clock();
//	// 以引用作为函数的返回值类型
//	size_t begin2 = clock();
//	for (size_t i = 0; i < 100000; ++i)
//		TestFunc2();
//	size_t end2 = clock();
//	// 计算两个函数运算完成之后的时间
//	cout << "TestFunc1 time:" << end1 - begin1 << endl;
//	cout << "TestFunc2 time:" << end2 - begin2 << endl;
//}
//
//int main() {
//	TestReturnByRefOrValue();
//}



//typedef struct SeqList {
//	int a[10];
//	int size;
//}SeqList;

////C接口设计
////读取第i个位置的值
//int SLAT(SeqList* ps, int i) {
//	assert(i < ps->size);
//	return ps->a[i];
//}
////修改第i个位置的值
//void SLModify(SeqList* ps, int i, int x) {
//	assert(i < ps->size);
//	ps->a[i] = x;
//}

////CPP接口设计
////读或修改第i个位置的值
//int& SLAT(SeqList& ps, int i) {
//	assert(i < ps.size);
//	return ps.a[i];
//}
//
//int main() {
//	SeqList s;
//	s.size = 3;
//	SLAT(s, 0) = 10;//修改第0个位置。SLAT有引用返回，所以可以赋值
//	SLAT(s, 1) = 20;
//	SLAT(s, 2) = 30;
//	cout << SLAT(s, 0) << endl;//直接访问并打印引用返回
//	cout << SLAT(s, 1) << endl;
//	cout << SLAT(s, 2) << endl;
//}
//传引用传参（任何时候都可以用）
//1.提高效率
//2.输出型参数（形参的修改，影响实参）

//传引用返回
//1.提高效率
//2.修改返回对象


#define N 10
struct SeqList {
	//成员函数
	int& at(int i) {
		assert(i < N);
		return a[i];
	}

	int& operator[](int i) {
		assert(i < N);
		return a[i];
	}

	//成员变量
	int a[N];
};

int main() {
	//struct SeqList s1;//兼容C的用法
	SeqList s2;

	for (size_t i = 0; i < N; i++)
		//s2.at(i) = i;
		s2[i] = i; //等效于s2.operator[](i) = i;

	for (size_t i = 0; i < N; i++)
		//cout << s2.at(i) << " ";
		cout << s2[i] << " ";
	cout << endl;
}